#include "vpaspcap.h"
#include <ctype.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdbool.h>
#include <getopt.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <inttypes.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <linux/wireless.h>
#include <linux/if.h>
#include <ifaddrs.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netpacket/packet.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/cmac.h>
#include <syslog.h>
#include <pthread.h>

#include "airkiss.h"
#include "ieee80211.h"

#if DEBUG
#define pcap_log(x, ...) printf(__VA_ARGS__)
#else
#define pcap_log    syslog
#endif

typedef struct akproc_st {
    int sockfd;
    int inited;
    airkiss_context_t airkiss;
    int running;
    char* ifname;
    int akst;
    //int to;
    pthread_t pid;
    pthread_mutex_t mutex;
} akproc_st;

vps_ap* iw_scanning()
{
    return NULL;
}

int set_channel(int sockfd, char const* ifname, int channel)
{
    static struct iwreq pwrq;

    memset(&pwrq, 0, sizeof(pwrq));
    strncpy(pwrq.ifr_name, ifname, IFNAMSIZ -1);
    pwrq.u.freq.e = 0;
    pwrq.u.freq.flags = IW_FREQ_FIXED;
    pwrq.u.freq.m = channel;
    if (ioctl(sockfd, SIOCSIWFREQ, &pwrq) < 0) {
        pcap_log(LOG_ERR, "Failed to set channel, ioctl(SIOCSIWFREQ) not supported by driver: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

/*===========================================================================*/
static struct ifreq ifr_old;
static struct iwreq iwr_old;
static uint8_t mac_orig[6];
static char drivername[34];
static char driverversion[34];
static char driverfwversion[34];
int opensocket(char const* ifname)
{
    static struct ethtool_perm_addr *epmaddr;
    static struct ifreq ifr;
    static struct iwreq iwr;
    static struct iw_param param;
    static struct sockaddr_ll ll;
    static struct packet_mreq mr;
    static struct ethtool_drvinfo drvinfo;
    int sockfd;

    memset(&mac_orig, 0, 6);
    memset(drivername, 0, sizeof(drivername));
    memset(driverversion, 0, sizeof(driverversion));
    memset(driverfwversion, 0, sizeof(driverfwversion));

    if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0)
    {
        pcap_log(LOG_ERR, "socket failed (do you have root privileges?): %s\n", strerror(errno));
        return -1;
    }

    memset(&ifr_old, 0, sizeof(ifr));
    strncpy(ifr_old.ifr_name, ifname, IFNAMSIZ -1);
    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr_old) < 0)
    {
        pcap_log(LOG_ERR, "failed to backup current interface flags, ioctl(SIOCGIFFLAGS) not supported by driver: %s\n", strerror(errno));
        close(sockfd);
        return -1;
    }

    memset(&iwr_old, 0, sizeof(iwr_old));
    strncpy(iwr_old.ifr_name, ifname, IFNAMSIZ -1);
    if (ioctl(sockfd, SIOCGIWMODE, &iwr_old) < 0)
    {
        pcap_log(LOG_ERR, "failed to backup  current interface mode, ioctl(SIOCGIWMODE) not supported by driver: %s\n", strerror(errno));
        close(sockfd);
        return -1;
    }

    if ((iwr_old.u.mode & IW_MODE_MONITOR) != IW_MODE_MONITOR)
    {
        memset(&ifr, 0, sizeof(ifr));
        strncpy( ifr.ifr_name, ifname, IFNAMSIZ -1);
        if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0)
        {
            pcap_log(LOG_ERR, "failed to get current interface flags, ioctl(SIOCGIFFLAGS) not supported by driver: %s\n", strerror(errno));
            close(sockfd);
            return -1;
        }
        ifr.ifr_flags = 0;
        if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0)
        {
            pcap_log(LOG_ERR, "failed to set interface down, ioctl(SIOCSIFFLAGS) not supported by driver: %s\n", strerror(errno));
            close(sockfd);
            return -1;
        }

        memset(&iwr, 0, sizeof(iwr));
        strncpy( iwr.ifr_name, ifname, IFNAMSIZ -1);
        if (ioctl(sockfd, SIOCGIWMODE, &iwr) < 0)
        {
            pcap_log(LOG_ERR, "failed to get interface information, ioctl(SIOCGIWMODE) not supported by driver: %s\n", strerror(errno));
            close(sockfd);
            return -1;
        }
        iwr.u.mode = IW_MODE_MONITOR;
        if (ioctl(sockfd, SIOCSIWMODE, &iwr) < 0)
        {
            pcap_log(LOG_ERR, "failed to set monitor mode, ioctl(SIOCSIWMODE) not supported by driver: %s\n", strerror(errno));
            close(sockfd);
            return -1;
        }
        memset(&iwr, 0, sizeof(iwr));
        strncpy( iwr.ifr_name, ifname, IFNAMSIZ -1);
        if (ioctl(sockfd, SIOCGIWMODE, &iwr) < 0)
        {
            pcap_log(LOG_ERR, "failed to get interface information, ioctl(SIOCGIWMODE) not supported by driver: %s\n", strerror(errno));
            close(sockfd);
            return -1;
        }
        if ((iwr.u.mode & IW_MODE_MONITOR) != IW_MODE_MONITOR)
        {
            pcap_log(LOG_ERR, "interface is not in monitor mode: 0x%x\n", iwr.u.mode);
            if (iwr.u.mode != IW_MODE_AUTO) {
                close(sockfd);
                return -1;
            }
        }

        ifr.ifr_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
        if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0)
        {
            pcap_log(LOG_ERR, "failed to set interface up, ioctl(SIOCSIFFLAGS) not supported by driver: %s\n", strerror(errno));
            close(sockfd);
            return -1;
        }
        memset(&ifr, 0, sizeof(ifr));
        strncpy( ifr.ifr_name, ifname, IFNAMSIZ -1);
        if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0)
        {
            pcap_log(LOG_ERR, "failed to get interface flags, ioctl(SIOCGIFFLAGS) not supported by driver: %s\n", strerror(errno));
            close(sockfd);
            return -1;
        }
        if ((ifr.ifr_flags & (IFF_UP)) != (IFF_UP))
        {
            pcap_log(LOG_ERR, "interface may not be operational\n");
            close(sockfd);
            return -1;
        }
    }
    else
        pcap_log(LOG_ERR, "interface is already in monitor mode\n");

    /* disable power management, if possible */
    memset(&iwr, 0, sizeof(iwr));
    strncpy( iwr.ifr_name, ifname, IFNAMSIZ -1);
    memset(&param,0 , sizeof(param));
    iwr.u.data.pointer = &param;
    ioctl(sockfd, SIOCSIWPOWER, &iwr);

    memset(&ifr, 0, sizeof(ifr));
    strncpy( ifr.ifr_name, ifname, IFNAMSIZ -1);
    ifr.ifr_flags = 0;
    if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0)
    {
        pcap_log(LOG_ERR, "failed to get SIOCGIFINDEX, ioctl(SIOCGIFINDEX) not supported by driver: %s\n", strerror(errno));
        close(sockfd);
        return -1;
    }

    memset(&ll, 0, sizeof(ll));
    ll.sll_family = PF_PACKET;
    ll.sll_ifindex = ifr.ifr_ifindex;
    ll.sll_protocol = htons(ETH_P_ALL);
    ll.sll_halen = ETH_ALEN;
    ll.sll_pkttype = PACKET_OTHERHOST | PACKET_OUTGOING;
    if (bind(sockfd, (struct sockaddr*) &ll, sizeof(ll)) < 0)
    {
        pcap_log(LOG_ERR, "failed to bind socket: %s\n", strerror(errno));
        close(sockfd);
        return -1;
    }

    memset(&mr, 0, sizeof(mr));
    mr.mr_ifindex = ifr.ifr_ifindex;
    mr.mr_type = PACKET_MR_PROMISC;
    if (setsockopt(sockfd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0)
    {
        pcap_log(LOG_ERR, "failed to set setsockopt(PACKET_MR_PROMISC): %s\n", strerror(errno));
        close(sockfd);
        return -1;
    }

    epmaddr = (struct ethtool_perm_addr*)calloc(1, sizeof(struct ethtool_perm_addr) +6);
    if(!epmaddr)
    {
        pcap_log(LOG_ERR, "failed to malloc memory for permanent hardware address: %s\n", strerror(errno));
        close(sockfd);
        return -1;
    }
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, ifname, IFNAMSIZ -1);
    epmaddr->cmd = ETHTOOL_GPERMADDR;
    epmaddr->size = 6;
    ifr.ifr_data = (char*)epmaddr;
    if(ioctl(sockfd, SIOCETHTOOL, &ifr) < 0)
    {
        pcap_log(LOG_ERR, "failed to get permanent hardware address, ioctl(SIOCETHTOOL) not supported by driver: %s\n", strerror(errno));
        close(sockfd);
        free(epmaddr);
        return -1;
    }
    if(epmaddr->size != 6)
    {
        pcap_log(LOG_ERR, "failed to get permanent hardware address length\n");
        close(sockfd);
        free(epmaddr);
        return -1;
    }
    memcpy(&mac_orig, epmaddr->data, 6);
    free(epmaddr);

    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, ifname, IFNAMSIZ -1);
    drvinfo.cmd = ETHTOOL_GDRVINFO;
    ifr.ifr_data = (char*)&drvinfo;
    if(ioctl(sockfd, SIOCETHTOOL, &ifr) < 0)
    {
        pcap_log(LOG_ERR, "failed to get driver information, ioctl(SIOCETHTOOL) not supported by driver: %s\n", strerror(errno));
        close(sockfd);
        return -1;
    }
    memcpy(&drivername, drvinfo.driver, 32);
    memcpy(&driverversion, drvinfo.version, 32);
    memcpy(&driverfwversion, drvinfo.fw_version, ETHTOOL_FWVERS_LEN);
    return sockfd;
}

void closesocket(int sockfd, char const* ifname)
{
    static struct ifreq ifr;
    if (sockfd >= 0)
    {
        memset(&ifr, 0, sizeof(ifr));
        strncpy( ifr.ifr_name, ifname, IFNAMSIZ -1);
        if(ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) pcap_log(LOG_ERR, "failed to get interface information: %s\n", strerror(errno));
        ifr.ifr_flags = 0;
        if(ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) pcap_log(LOG_ERR, "failed to set interface down: %s\n", strerror(errno));
        if(ioctl(sockfd, SIOCSIWMODE, &iwr_old) < 0) pcap_log(LOG_ERR, "failed to restore old SIOCSIWMODE: %s\n", strerror(errno));
        if(ioctl(sockfd, SIOCSIFFLAGS, &ifr_old) < 0) pcap_log(LOG_ERR, "failed to restore old SIOCSIFFLAGS and to bring interface up: %s\n", strerror(errno));
        if(close(sockfd) != 0) pcap_log(LOG_ERR, "failed to close raw socket: %s\n", strerror(errno));
        sockfd = -1;
    }
}

int read_packet(int sockfd, void* data, unsigned int sz, struct timeval* tvf)
{
    static fd_set readfds;
    FD_ZERO(&readfds);
    FD_SET(sockfd, &readfds);
    int fdnum = select(sockfd + 1, &readfds, NULL, NULL, tvf);
    if (fdnum > 0 && FD_ISSET(sockfd, &readfds)) {
        //pcap_log(LOG_DEBUG, "reading....");
        return read(sockfd, data, sz);//recv(sockfd, data, sz, 0);
    }
    return fdnum;
}
